﻿#region license

/* 
    -------------------------------------------------------------------------------
                     Copyright 2013 Gabriele Gindro 
    -------------------------------------------------------------------------------
  
  	- Versione 1.6
   
    (NEW) Nei metodi di Select è stata aggiunta la clausola LIMIT per offrire ancora più personalizzazione alla selezione dei valori
    (NEW) Nuovo metodo GetTables(...) per ottenere informazioni dettaliate su tutte le tabelle di un database
    (NEW) Nuovo metodo RenameTable(...) per rinominare una tabella
    (NEW) Nuovo metodo RenameColumn(...) per rinominare una colonna
    (NEW) Nuovo metodo OrderColumn(...) per ordinare una colonna 
    (NEW) Nuovo metodo ShowVariables(...) per visualizzare tutte le varibili MySql globali e/o di sessione
    (NEW) Nuovo metodo GetVariable(...) per prelevare il valore di una variabile MySql globale e/o di sessione
    (NEW) Nuovo metodo SetVariable(...) per settare il valore di una variabile MySql globale e/o di sessione
    (NEW) Nuovo metodo ExistsVariable(...) per verificare se una variabile MySQl globale e/o di sessione esiste

    (CHANGE) I metodi execQuery(...) e readQuery(...) sono ora pubblici per massimizzare al massimo la personalizzazione di una query. In questo modo potete eseguire direttamente la vostra query senza passare dai metodi della classe
  	(CHANGE) I metodi GetList(...), GetDictionaryByRow(...) e GetDictionaryByColumn(...) sono ora pubblici per predisporre un metodo di conversione per query personalizzate che restituiscono dei valori.
    (CHANGE / IMPROVE) Per semplificare l'assegnamento dei campi/valori, nel metodo Insert(...) i parametri string[] sono stati sostituiti con il parametro Dictionary<string, string> dove la chiave è il nome della colonna, mentre il valore è il valore della colonna

    (FIX) Nei metodi di Select la clausola ORDER BY se messa a null sollecitava l'eccezione "SystemNullReferenceException"

  	-------------------------------------------------------------------------------
  	
  	Dipendenze:
  	
  	- MySql Connector Net 6.7.4.0 (MySql.Data.dll)
  	
 	- Microsoft .NET Framework versione 4.0 o successive
 	
 	- Sistemi x86 e x64
	
  	Questo programma è stato creato con Visual Studio 2012 (Update 3), Windows 7
  	
    -------------------------------------------------------------------------------
    
 */

#endregion

#region imports

using System;
using System.Collections.Generic;
using MySql.Data.MySqlClient;
using System.Data;
using System.Xml;
using System.Linq;

#endregion

namespace mysqllib
{
	
	#region classes

    /// <summary>
    /// Enum dei tipi delle variabili MySql
    /// </summary>
    public enum MySqlVariablesType
    {
        All,
        Global,
        Session
    }

    /// <summary>
    /// Classe Table per le informazioni dettagliate su una tabella
    /// </summary>
    public class MySqlTable
    {
        /// <summary>
        /// Nome
        /// </summary>
        public string Name { get; internal set; }
        /// <summary>
        /// Tipo
        /// </summary>
        public string Type { get; internal set; }
        /// <summary>
        /// Motore
        /// </summary>
        public string Engine { get; internal set; }
        /// <summary>
        /// Data di creazione
        /// </summary>
        public string CreateTime { get; internal set; }
        /// <summary>
        /// Data di aggiornamento
        /// </summary>
        public string UpdateTime { get; internal set; }
        /// <summary>
        /// Collation
        /// </summary>
        public string Collation { get; internal set; }
        /// <summary>
        /// Commento
        /// </summary>
        public string Comment { get; internal set; }
        /// <summary>
        /// Numero di righe presenti
        /// </summary>
        public int Rows { get; internal set; }
        /// <summary>
        /// Lista delle colonne presenti
        /// </summary>
        public List<MySqlColumn> Columns { get; internal set; }
    }

    /// <summary>
    /// Classe Column per le informazioni dettagliate su una colonna
    /// </summary>
    public class MySqlColumn
    {
        /// <summary>
        /// Nome
        /// </summary>
        public string Name { get; internal set; }
        /// <summary>
        /// Ordine di posizione rispetto alle altre
        /// </summary>
        public int OrdinalPosition { get; internal set; }
        /// <summary>
        /// Tipo
        /// </summary>
        public string Type { get; internal set; }
        /// <summary>
        /// Nome del character
        /// </summary>
        public string CharacterSetName { get; internal set; }
        /// <summary>
        /// Nome del collation
        /// </summary>
        public string CollationName { get; internal set; }
        /// <summary>
        /// Commento
        /// </summary>
        public string Comment { get; internal set; }
        /// <summary>
        /// Valori della colonna
        /// </summary>
        public List<string> Values { get; internal set; }
    }

	/// <summary>
	/// mysqllib è una libreria che raccoglie metodi per semplificare l'accesso e la gestione di un database mysql.
	/// </summary>
	public class MySqlManager
	{
		#region variables
		
		/// <summary>
		/// Informazione pubblica: Versione della libreria
		/// </summary>
		public readonly string lib_version = "1.6";
		
		/// <summary>
		/// Informazione pubblica: Versione minima richiesta di MySql .Net Connector
		/// </summary>
		public readonly string mysql_net_version = "6.7.4.0";
		
		public string dbhost { get; private set; }
		public string dbname { get; private set; }
		public string dbuser { get; private set; }
		public string dbpass { get; private set; }
		public int dbport { get; private set; }
		public sslMode dbsslmode { get; private set; }
		public bool dbcompression { get; private set; }
		public protocolMode dbprotocolmode { get; private set; }
		public int dbtimeout { get; private set; }
		
		public enum sslMode { Preferred, Required, none }
		public enum protocolMode { socket, tcp, pipe, unix, memory }
		public enum connection_status { valid, invalid }
		
		public connection_status status { get; private set; }
		
		private string connection_string;
		
		private MySqlConnection connection;
		
		#endregion
		
		#region metodhs
		
		/// <summary>
		/// Inizializza una nuova istanza di MySql
		/// </summary>
		public MySqlManager()
		{
			setDefaultVariables();
			status = connection_status.invalid;
		}
		
		/// <summary>
		/// Imposta ai valori default tutte le variabili di istanza e non
		/// </summary>
		private void setDefaultVariables()
		{
			dbhost = "";
			dbname = "";
			dbuser = "";
			dbpass = "";
			dbport = 0;
			dbsslmode = sslMode.none;
			dbcompression = false;
			dbprotocolmode = protocolMode.socket;
			dbtimeout = 30;
			connection_string = "";
		}
		
		/// <summary>
		/// Imposta una nuova connessione ad un nuovo database. Un eventuale connessione precedente verrà chiusa.
		/// </summary>
		/// <param name="host">Host del server (IP o indirizzo http)</param>
		/// <param name="db">Nome del database</param>
		/// <param name="user">Username</param>
		/// <param name="pass">Password</param>
		/// <param name="port">Indica la porta remota di connessione (Opzionale)</param>
		/// <param name="sslmode">Indica se utilizzare una connessione ssl al server specificandone il tipo (di default è none)</param>
		/// <param name="compression">Indica se utilizzare la compressione dei dati (di default è false)</param>
		/// <param name="protocolmode">Indica quale tipo di protocollo utilizzare (di default è socket)</param>
		public void Connect(string host, string db, string user, string pass, int port = -1, sslMode sslmode = sslMode.none, bool compression = false, protocolMode protocolmode = protocolMode.socket)
		{
			if (status == connection_status.valid)
				Close();
			connection_string = "Server=" + host + 
								";Database=" + db + 
								";Uid=" + user + 
								";Pwd=" + pass + 
								((port > -1) ? ";Port=" + port.ToString() : "") +
								((sslmode != sslMode.none) ? ";SslMode=" + sslmode.ToString() : "") +
								";UseCompression=" + compression +
								";Protocol=" + protocolmode +
								";Connection Timeout=" + dbtimeout.ToString();
			try
			{
				using (connection = new MySqlConnection())
				{
					connection.ConnectionString = connection_string;
					connection.Open();
				}	
			}
			catch (MySqlException err)
			{
                throw new ArgumentException(err.Message);
			}
            dbhost = host;
            dbname = db;
            dbuser = user;
            dbpass = pass;
            dbport = port;
            dbsslmode = sslmode;
            dbcompression = compression;
            dbprotocolmode = protocolmode;
			status = connection_status.valid;
		}
		
		/// <summary>
		/// Controlla se esiste un database
		/// </summary>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
		/// <returns>true se il database esiste, false se non esiste, altrimenti viene sollecitato l'errore della query</returns>
		public bool ExistDatabase(string db = "")
		{
            DataTable res = readQuery("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            return (res.Rows.Count > 0) ? true : false; 
		}
		
		/// <summary>
		/// Controlla se esiste una tabella nel database corrente
		/// </summary>
		/// <param name="table">Nome della tabella</param>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
        /// <returns>true se la tabella esiste, false se non esiste, altrimenti viene sollecitato l'errore della query</returns>
		public bool ExistsTable(string table, string db = "")
		{
            DataTable res = readQuery("SELECT COUNT(*) FROM information_schema.tables AS isc WHERE isc.table_name = '" + table + "' AND isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            return (res.Rows.Count > 0) ? true : false;	
		}
		
		/// <summary>
		/// Controlla se esiste una colonna nella tabella specificata
		/// </summary>
		/// <param name="table">Nome della tabella</param>
		/// <param name="column">Nome della colonna</param>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
        /// <returns>true se la colonna esiste, false se non esiste, altrimenti viene sollecitato l'errore della query</returns>
		public bool ExistsColumn(string table, string column, string db = "")
		{
			DataTable res = readQuery("SELECT COUNT(*) FROM information_schema.columns AS isc WHERE isc.column_name = '" + column + "' AND isc.table_name = '" + table + "' AND isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            return (res.Rows.Count > 0) ? true : false;	
		}
		
		/// <summary>
		/// Controlla se esiste un valore nel campo specificato
		/// </summary>
		/// <param name="table">Nome della tabella</param>
		/// <param name="column">Nome del campo</param>
		/// <param name="val">Valore da controllare</param>
		/// <param name="_where">Condizione per la scelta del valore. Se omessa cerca tutte le occorrenze senza applicare nessun filtro. (Opzionale)</param>
        /// <returns>true se il campo esiste false se non esiste, altrimenti viene sollecitato l'errore della query</returns>
		public bool ExistsValue(string table, string column, string val, string _where = "")
		{
            List<string> res = SelectSingle(table, column, column + "='" + val + "'" + ((!string.IsNullOrEmpty(_where)) ? " AND " + _where : ""));
            return (res.Count > 0) ? true : false;
		}

        /// <summary>
        /// Ottiene informazioni dettagliate di tutte le tabelle di un database specificato
        /// </summary>
        /// <param name="db">Nome del database</param>
        /// <returns>Lista di MySqlTable</returns>
        public List<MySqlTable> GetTables(string db = "")
        {
            List<string> result = GetList(readQuery("SHOW TABLES FROM " + ((!string.IsNullOrEmpty(db)) ? db : dbname)), 0, true);
            List<MySqlTable> tables = new List<MySqlTable>();
            for (int i = 0; i < result.Count; i++)
                tables.Add(GetTable(result[i]));
            return tables;
        }

        /// <summary>
        /// Ottiene informazioni dettagliate di una tabella
        /// </summary>
        /// <param name="table">Nome della tabella</param>
        /// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
        /// <returns>MySqlTable con tutti i dettagli sulla tabella specificata</returns>
        public MySqlTable GetTable(string table, string db = "")
        {
            Dictionary<int, List<string>> result = SelectMultipleByRow("information_schema.tables AS isc", new string[] { "isc.table_type", "isc.engine", "isc.table_rows", "isc.create_time", "isc.update_time", "isc.table_collation", "isc.table_comment" }, "isc.table_name = '" + table + "' AND isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            if (result.Keys.Count == 0)
                throw new ArgumentException("Table " + table + " in " + ((!string.IsNullOrEmpty(db)) ? db : dbname) + " does not exists");
            MySqlTable mysqltable = new MySqlTable();
            mysqltable.Name = table;
            foreach (KeyValuePair<int, List<string>> k in result)
            {
                mysqltable.Type = k.Value[0];
                mysqltable.Engine = k.Value[1];
                mysqltable.Rows = Convert.ToInt32(k.Value[2]);
                mysqltable.CreateTime = k.Value[3];
                mysqltable.UpdateTime = k.Value[4];
                mysqltable.Collation = k.Value[5];
                mysqltable.Comment = k.Value[6];
            }
            mysqltable.Columns = new List<MySqlColumn>();
            List<string> columns = SelectSingle("information_schema.columns AS isc", "isc.column_name", "isc.table_name = '" + table + "' AND isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            if (columns.Count > 0)
            {
                for (int i = 0; i < columns.Count; i++)
                    mysqltable.Columns.Add(GetColumn(table, columns[i], db));
            }
            return mysqltable;
        }

        /// <summary>
        /// Ottiene informazioni dettagliate di una colonna, se il database specificato non è quello corrente le informazioni sui valori della colonna non possono essere prelevati
        /// </summary>
        /// <param name="table">Nome della tabella</param>
        /// <param name="column">Nome della colonna</param>
        /// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
        /// <returns>MySqlColumn con tutti i dettagli sulla colonna della tabella specificata</returns>
        public MySqlColumn GetColumn(string table, string column, string db = "")
        {
            Dictionary<int, List<string>> result = SelectMultipleByRow("information_schema.columns AS isc", new string[] { "isc.ordinal_position", "isc.character_set_name", "isc.collation_name", "isc.column_type", "isc.column_comment" }, "isc.table_name = '" + table + "' AND isc.column_name = '" + column + "' AND isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            if (result.Keys.Count == 0)
                throw new ArgumentException("Column " + column + " in table " + table + " in " + ((!string.IsNullOrEmpty(db)) ? db : dbname) + " does not exists");
            MySqlColumn mysqlcolumn = new MySqlColumn();
            mysqlcolumn.Name = column;
            foreach (KeyValuePair<int, List<string>> k in result)
            {
                mysqlcolumn.OrdinalPosition = Convert.ToInt32(k.Value[0]);
                mysqlcolumn.CharacterSetName = k.Value[1];
                mysqlcolumn.CollationName = k.Value[2];
                mysqlcolumn.Type = k.Value[3];
                mysqlcolumn.Comment = k.Value[4];
            }
            if (!string.IsNullOrEmpty(db))
                mysqlcolumn.Values = SelectSingle(table, column);
            else
                mysqlcolumn.Values = new List<string>();
            return mysqlcolumn;
        }
		
		/// <summary>
		/// Imposta il type della colonna specificata
		/// </summary>
		/// <param name="table">Nome della tabella</param>
		/// <param name="column">Nome della colonna</param>
		/// <param name="type">Tipo della colonna</param>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
		public void setColumnType(string table, string column, string type, string db = "")
		{
	        execQuery("UPDATE information_schema.columns AS isc SET isc.column_type = '" + type + "' WHERE isc.table_schema = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'" + " AND isc.table_name = '" + table + "' AND isc.column_name = '" + column + "'");
		}
		
		/// <summary>
		/// Ottiene il tipo di carattere utilizzato nel database specificato
		/// </summary>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
        /// <returns>stringa contenente i risultati (default_character_set_name,default_collation_name), altrimenti un'eccezione se la query contiene un errore</returns>
		public string getDefaultCharacterDatabase(string db = "")
		{
			Dictionary<int, List<string>> res = SelectMultipleByRow("information_schema.schemata AS isc", new string[] { "isc.default_character_set_name",  "isc.default_collation_name" }, "isc.schema_name = '" + ((!string.IsNullOrEmpty(db)) ? db : dbname) + "'");
            if (res.Keys.Count == 0)
                throw new ArgumentException("Database " + ((!string.IsNullOrEmpty(db)) ? db : dbname) + " does not exist");
            string result = "";
            foreach (KeyValuePair<int, List<string>> k in res)
            {
                result = k.Value[0] + "," + k.Value[1];
            }
            return result;    
		}
		
		/// <summary>
		/// Imposta una nuova codifica di caratteri del database specificato
		/// </summary>
		/// <param name="character">Gruppo di un carattere</param>
		/// <param name="collate">Tipo di carattere specifico</param>
		/// <param name="db">Nome del database. Se omesso viene utilizzato il database corrente. (Opzionale)</param>
		public void setDefaultCharacterDatabase(string character, string collate, string db = "")
		{
			execQuery("ALTER DATABASE " + ((!string.IsNullOrEmpty(db)) ? db : dbname) + " DEFAULT CHARACTER SET " + character + " DEFAULT COLLATE " + collate);
		}

        /// <summary>
        /// Svuota una tabella nel database corrente mantendendo intatte le colonne.
        /// </summary>
        /// <param name="table">Nome della tabella da svuotare</param>
        public void TruncateTable(string table)
        {
            if (!ExistsTable(table))
                throw new ArgumentException("Table " + table + " does not exist");
            execQuery("TRUNCATE TABLE " + table);
        }

        /// <summary>
        /// Svuota tutto il database corrente
        /// </summary>
        public void TruncateDatabase()
        {
            if (!ExistDatabase(dbname))
                throw new ArgumentException("Database " + dbname + " does not exists");
            List<string> tables = SelectSingle("information_schema.tables AS isc", "isc.table_name", "isc.table_schema = '" + dbname + "'");
            for (int i = 0; i < tables.Count; i++)
                TruncateTable(tables[i]);
        }

        /// <summary>
        /// Esegue un update singolo sul database
        /// </summary>
        /// <param name="table">Tabella su cui applicare l'update</param>
        /// <param name="field">Nome del campo da aggiornare</param>
        /// <param name="val">Nuovo valore da assegnare</param>
        /// <param name="_where">Imposta una condizione di update, se omessa vengono modificate tutte i campi che contento il valore specificato (Opzionale)</param>
        public void UpdateSingle(string table, string field, string val, string _where = "")
		{
			execQuery("UPDATE " + table + " SET " + field + "='" + val + "'" + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : ""));
		}
		
		/// <summary>
		/// Esegue un update multiplo sul database
		/// </summary>
		/// <param name="table">Tabella su cui applicare l'update</param>
		/// <param name="values">Lista di coppie di valori [ colonna - valore ]</param>
		/// <param name="_where">Imposta una condizione di update (Opzionale)</param></param>
		public void UpdateMultiple(string table, Dictionary<string, string> values, string _where = "")
		{
			string data = "";
			foreach (KeyValuePair<string, string> elem in values)
				data += elem.Key + "='" + elem.Value + "',";
			data = data.Remove(data.Length-1,1);
			execQuery("UPDATE " + table + " SET " + data + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : ""));
		}

        /// <summary>
        /// Rinomina una tabella
        /// </summary>
        /// <param name="oldtable">Nome corrente della tabella</param>
        /// <param name="newtable">Nuovo nome da assegnare alla tabella</param>
        public void RenameTable(string oldtable, string newtable)
        {
            if (!ExistsTable(oldtable, dbname))
                throw new ArgumentException("Table '" + oldtable + "' does not exists");
            execQuery("RENAME TABLE " + oldtable + " TO " + newtable); 
        }

        /// <summary>
        /// Rinomina una colonna di una tabella
        /// </summary>
        /// <param name="table">Nome della tabella</param>
        /// <param name="oldcolumn">Nome corrente della colonna</param>
        /// <param name="newcolumn">Nuovo nome della colonna</param>
        public void RenameColumn(string table, string oldcolumn, string newcolumn)
        {
            if (!ExistsColumn(table, oldcolumn, dbname))
                throw new ArgumentException("The column '" + oldcolumn + "' in table '" + table + "' does not exists");
            string type = GetColumn(table, oldcolumn, dbname).Type;
            execQuery("ALTER TABLE " + table + " CHANGE " + oldcolumn + " " + newcolumn + " " + type);
        }
		
		/// <summary>
		/// Inserisci alcuni dati nella tabella specificata
		/// </summary>
		/// <param name="table">Tabella su cui applicare l'insert</param>
		/// <param name="fields">Array di stringhe. Deve contenere i campi su cui aggiungere i valori</param>
		/// <param name="values">Array di stringhe. Deve contenere i nuovi valori. La posizione dell'elemento nell'array deve corrispondere alla posizione delle'elemento dell'array campo. campo[0] = valore[0]</param>
		/// <param name="_where">Imposta una condizione di insert (Opzionale)</param>
		public void Insert(string table, Dictionary<string, string> values, string _where = "")
		{
			string f = "";
			string v = "";
            foreach (KeyValuePair<string, string> fields in values)
                f += fields.Key + ",";
            f = f.Remove(f.Length - 1);
            foreach (KeyValuePair<string, string> vals in values)
                v += "'" + vals.Value + "',";
			v = v.Remove(v.Length-1);
			execQuery("INSERT INTO " + table + " (" + f + ") VALUES (" + v + ")" + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : ""));
		}
		
		/// <summary>
		/// Inserisci una nuova riga compilando tutti i campi nella tabella specificata
		/// </summary>
		/// <param name="table">Tabella su cui applicare l'insert</param>
		/// <param name="values">Array di stringhe. Deve contenere i nuovi valori. Il numero totale dei valori deve corrispondere al numero totale di colonne della tabella</param>
		/// <param name="_where">Imposta una condizione di insert (Opzionale)</param>
		public void InsertAll(string table, string[] values, string _where = "")
		{
			string data = "";
			for (int i = 0; i < values.Length; i++)
				data += "'" + values[i] + "',";
			data = data.Remove(data.Length-1);
			execQuery("INSERT INTO " + table + " VALUES (" + data + ")" + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : ""));
		}
		
		/// <summary>
		/// Seleziona i valori di una singola colonna della tabella specificata
		/// </summary>
		/// <param name="table">Tabella da cui estrapolare le informazioni</param>
		/// <param name="column">Nome della colonna da cui estrapolare i valori</param>
		/// <param name="_where">Imposta una condizione di select (Opzionale)</param>
		/// <param name="_orderby">Imposta una condizione di ordine di selezione dei dati (Opzionale)</param>
        /// <param name="_limit">Imposta una condizione di limite dei risultati, es: per limitare ai primi 10 basta scrivere 10, per limitare i risultati da un range definito scrivere numero_inizio, numero_fine [5, 10], utilizzare insieme a ORDER BY per personalizzare ancora di più il filtro (Opzionale)</param>
		/// <param name="hiddenFieldCount">Indica se nella selezione vanno inclusi anche i campi nascosti (di default è true)</param>
		/// <returns>Dizionario di coppie di chiavi = nome della colonna, valori = valore della colonna, lista vuota se non esitono valori oppure un'eccezione se la query contiene un errore</returns>
		public List<string> SelectSingle(string table, string column, string _where = "", string _orderby = "", string _limit = "", bool hiddenFieldCount = true)
		{
			DataTable res = readQuery("SELECT " + column + " FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : "") +
                ((!string.IsNullOrEmpty(_orderby)) ? " ORDER BY " + _orderby : "") + ((!string.IsNullOrEmpty(_limit)) ? " LIMIT " + _limit : ""));
            return GetList(res, 0, hiddenFieldCount);
		}
		
		/// <summary>
		/// Seleziona i valori di una o più colonne dalla tabella specificata restituendo i risultati per righe
		/// </summary>
		/// <param name="table">Tabella da cui estrapolare le informazioni</param>
		/// <param name="column">Array di stringhe. Deve contenere i nomi delle colonne da cui estrapolare i valori.</param>
		/// <param name="_where">Imposta una condizione di select (Opzionale)</param>
		/// <param name="_orderby">Imposta una condizione di ordine di selezione dei dati (Opzionale)</param>
        /// <param name="_limit">Imposta una condizione di limite dei risultati, es: per limitare ai primi 10 basta scrivere 10, per limitare i risultati da un range definito scrivere numero_inizio, numero_fine [5, 10], utilizzare insieme a ORDER BY per personalizzare ancora di più il filtro (Opzionale)</param>
		/// <param name="hiddenFieldCount">Indica se nella selezione vanno inclusi anche i campi nascosti (di default è true)</param>
        /// <returns>Dizionario di coppie di chiavi = numero riga, valori = valori della riga, lista vuota se non esitono valori oppure un'eccezione se la query contiene un errore</returns>
		public Dictionary<int, List<string>> SelectMultipleByRow(string table, string[] column, string _where = "", string _orderby = "", string _limit = "", bool hiddenFieldCount = true)
		{
			string col = "";
			for (int i = 0; i < column.Length; i++)
				col += column[i] + ",";
			col = col.Remove(col.Length-1,1);
			DataTable res = readQuery("SELECT " + col + " FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : "") +
                                     ((!string.IsNullOrEmpty(_orderby)) ? " ORDER BY " + _orderby : "") + ((!string.IsNullOrEmpty(_limit)) ? " LIMIT " + _limit : ""));

            return GetDictonaryByRow(res, hiddenFieldCount);
        }

        /// <summary>
        /// Seleziona i valori di una o più colonne dalla tabella specificata restituendo i valori per colonne
        /// </summary>
        /// <param name="table">Tabella da cui estrapolare le informazioni</param>
        /// <param name="column">Array di stringhe. Deve contenere i nomi delle colonne da cui estrapolare i valori.</param>
        /// <param name="_where">Imposta una condizione di select (Opzionale)</param>
        /// <param name="_orderby">Imposta una condizione di ordine di selezione dei dati (Opzionale)</param>
        /// <param name="_limit">Imposta una condizione di limite dei risultati, es: per limitare ai primi 10 basta scrivere 10, per limitare i risultati da un range definito scrivere numero_inizio, numero_fine [5, 10], utilizzare insieme a ORDER BY per personalizzare ancora di più il filtro (Opzionale)</param>
        /// <param name="hiddenFieldCount">Indica se nella selezione vanno inclusi anche i campi nascosti (di default è true)</param>
        /// <returns>Dizionario di coppie di chiavi = nome della colonna, valori = valori della colonna, lista vuota se non esitono valori oppure un'eccezione se la query contiene un errore</returns>
        public Dictionary<string, List<string>> SelectMultipleByColumn(string table, string[] column, string _where = "", string _orderby = "", string _limit = "", bool hiddenFieldCount = true)
        {
            string col = "";
            for (int i = 0; i < column.Length; i++)
                col += column[i] + ",";
            col = col.Remove(col.Length - 1, 1);
            DataTable res = readQuery("SELECT " + col + " FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : "") +
                                     ((!string.IsNullOrEmpty(_orderby)) ? " ORDER BY " + _orderby : "") + ((!string.IsNullOrEmpty(_limit)) ? " LIMIT " + _limit : ""));

            return GetDictonaryByColumn(res, hiddenFieldCount);
        }
		
		/// <summary>
        /// Seleziona i valori di tutte le colonne della tabella specificata restituendo i risultati per righe
		/// </summary>
		/// <param name="table">Tabella da cui estrapolare le informazioni</param>
		/// <param name="_where">Imposta una condizione di select (Opzionale)</param>
		/// <param name="_orderby">Imposta una condizione di ordine di selezione dei dati (Opzionale)</param>
        /// <param name="_limit">Imposta una condizione di limite dei risultati, es: per limitare ai primi 10 basta scrivere 10, per limitare i risultati da un range definito scrivere numero_inizio, numero_fine [5, 10], utilizzare insieme a ORDER BY per personalizzare ancora di più il filtro (Opzionale)</param>
		/// <param name="hiddenFieldCount">Indica se nella selezione vanno inclusi anche i campi nascosti (di default è true)</param>
        /// <returns>Dizionario di coppie di chiavi = numero riga, valori = valori della riga, lista vuota se non esitono valori oppure un'eccezione se la query contiene un errore</returns>
        public Dictionary<int, List<string>> SelectAllByRow(string table, string _where = "", string _orderby = "", string _limit = "", bool hiddenFieldCount = true)
		{
			DataTable res = readQuery("SELECT *" + " FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : "") +
                                     ((!string.IsNullOrEmpty(_orderby)) ? " ORDER BY " + _orderby : "") + ((!string.IsNullOrEmpty(_limit)) ? " LIMIT " + _limit : ""));

            return GetDictonaryByRow(res, hiddenFieldCount);
        }

        /// <summary>
        /// Seleziona i valori di tutte le colonne della tabella specificata restituendo i risultati per colonne
        /// </summary>
        /// <param name="table">Tabella da cui estrapolare le informazioni</param>
        /// <param name="_where">Imposta una condizione di select (Opzionale)</param>
        /// <param name="_orderby">Imposta una condizione di ordine di selezione dei dati (Opzionale)</param>
        /// <param name="hiddenFieldCount">Indica se nella selezione vanno inclusi anche i campi nascosti (di default è true)</param>
        /// <returns>Dizionario di coppie di chiavi = nome della colonna, valori = valori della colonna, lista vuota se non esitono valori oppure un'eccezione se la query contiene un errore</returns>
        public Dictionary<string, List<string>> SelectAllByColumn(string table, string _where = "", string _orderby = "", string _limit = "", bool hiddenFieldCount = true)
        {
            DataTable res = readQuery("SELECT *" + " FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : "") +
                                     ((!string.IsNullOrEmpty(_orderby)) ? " ORDER BY " + _orderby : "") + ((!string.IsNullOrEmpty(_limit)) ? " LIMIT " + _limit : ""));

            return GetDictonaryByColumn(res, hiddenFieldCount);
        }
		
		/// <summary>
		/// Rimuove una o più righe dalla tabella specificata
		/// </summary>
		/// <param name="table">Tabella su cui applicare il delete</param>
		/// <param name="_where">Imposta una condizione di delete (Opzionale)</param>
		public void DeleteRow(string table, string _where = "")
		{
			execQuery("DELETE FROM " + table + ((!string.IsNullOrEmpty(_where)) ? " WHERE " + _where : ""));
		}
		
		/// <summary>
		/// Rimuove una tabella dal database
		/// </summary>
		/// <param name="table">Tabella da rimuovere</param>
		public void DeleteTable(string table)
		{
			execQuery("DROP TABLE IF EXISTS " + table);
		}
		
		/// <summary>
		/// Rimuove un database. Questo metodo comporta il Close() della connessione al database qual'ora sia lo stesso su cui si sta effettuando tale operazione
		/// </summary>
		/// <param name="db">Nome del database</param>
		public void DeleteDatabase(string db)
		{
            execQuery("DROP DATABASE IF EXISTS " + db);
            if (db.Equals(dbname))
                Close();
		}
		
		/// <summary>
		/// Rimuove una colonna nella tabella specificata
		/// </summary>
		/// <param name="table">Nome della tabella</param>
		/// <param name="column">Nome della colonna</param>
		public void DeleteColumn(string table, string column)
		{
			execQuery("ALTER TABLE " + table + " DROP COLUMN " + column);
		}
		
		/// <summary>
		/// Crea un nuovo database. Il database che si crea deve essere diverso da quello su cui si stanno effettuando le operazioni.
		/// </summary>
		/// <param name="db">Nome del database da creare</param>
		/// <param name="characterset">Gruppo di codifica dei caratteri es: utf8, latin1 ...</param>
		/// <param name="collation">Specifica del gruppo di codifica es: utf8_unicode_ci ...</param>
		public void CreateDatabase(string db, string characterset, string collation)
		{
			execQuery("CREATE DATABASE IF NOT EXISTS " + db + " DEFAULT CHARACTER SET '" + characterset + "' DEFAULT COLLATE '" + collation + "'");
		}
		
		/// <summary>
		/// Crea una nuova tabella
		/// </summary>
		/// <param name="table">Nome della tabella da creare</param>
		/// <param name="fields">Array di stringhe. Deve contenere i nomi delle colonne che si vuole creare</param>
		/// <param name="types">Array di stringhe. Deve contenere i tipi delle colonne associate al nome. La posizione dell'elemento dell'array deve corispondere all'elemento fields nella stessa posizione</param>
		/// <param name="type">Indica il tipo di tabella. es: InnoDB... (Opzionale)</param>
		public void CreateTable(string table, string[] fields, string[] types, string type = "")
		{
			string data = "(";
			for (int i = 0; i < fields.Length; i++)
				data += fields[i] + " " + types[i] + ",";
			data = data.Remove(data.Length-1,1) + ")";
			execQuery("CREATE TABLE IF NOT EXISTS " + table + data + ((type != "") ? " TYPE=" + type : ""));
		}
		
		/// <summary>
		/// Crea una nuova colonna nella tabella specificata
		/// </summary>
		/// <param name="table">Tabella in cui aggiungere una nuova colonna</param>
		/// <param name="column">Nome della colonna</param>
		/// <param name="type">Tipo di colonna. es: VARCHAR(60), INT(8) ...</param>
		/// <param name="position">Indica la posizione in cui creare la colonna. es: FIRST, AFTER name... (di default viene accodata all'ultima colonna)</param>
		public void CreateColumn(string table, string column, string type, string position = "")
		{
            execQuery("ALTER TABLE " + table + " ADD " + column + " " + type + " " + ((position != "") ? position : ""));	
		}

        /// <summary>
        /// Sposta l'ordine di una colonna
        /// </summary>
        /// <param name="table">Nome della tabella</param>
        /// <param name="column">Nome della colonna da spostare</param>
        /// <param name="aftercolumn">Colonna dopo la quale inserire quella spostata, se omessa viene spostata all'inizio di tutte (Opzionale)</param>
        public void OrderColumn(string table, string column, string aftercolumn = "")
        {
            if (!ExistsTable(table, dbname))
                throw new ArgumentException("The '" + table + "' does not exists");
            if (!ExistsColumn(table, column, dbname))
                throw new ArgumentException("The '" + column + "' column in '" + table + "' table does not exists");
            string type = GetColumn(table, column, dbname).Type;
            execQuery("ALTER TABLE " + table + " MODIFY COLUMN " + column + " " + type + ((!string.IsNullOrEmpty(aftercolumn)) ? " AFTER " + aftercolumn : "FIRST"));
        }

        /// <summary>
        /// Controlla l'esistenza di una variabile
        /// </summary>
        /// <param name="variable">Nome della variabile</param>
        /// <param name="type">Tipo della variabile</param>
        /// <returns>true se la variabile esiste, false altrimenti</returns>
        public bool ExistsVariable(string variable, MySqlVariablesType type)
        {
            string t = "";
            if (type == MySqlVariablesType.Global)
                t = "GLOBAL ";
            else if (type == MySqlVariablesType.Session)
                t = "SESSION ";
            return (GetList(readQuery("SHOW " + t + "VARIABLES LIKE '" + variable + "'"), 0, true).Count > 0) ? true : false; 
        }

        /// <summary>
        /// Ottiene il valore della variabile (globale o no) impostata
        /// </summary>
        /// <param name="variable">Nome della variabile</param>
        /// <param name="isGlobal">Indica se la variabile è globale oppure no</param>
        /// <returns>il valore della variabile, se esiste</returns>
        public string GetVariable(string variable, MySqlVariablesType type)
        {
            string t = "";
            if (type == MySqlVariablesType.Global)
                t = "GLOBAL ";
            else if (type == MySqlVariablesType.Session)
                t = "SESSION ";
            string result = GetList(readQuery("SHOW " + t + "VARIABLES LIKE '" + variable + "'"), 1, true)[0];
            if (string.IsNullOrEmpty(result))
                throw new ArgumentException("The " + t + "variable '" + variable + "' does not exists");
            return result;
        }

        /// <summary>
        /// Mostra tutte le variabili (globali e/o di sessione) correnti
        /// </summary>
        /// <param name="type">Tipo di variabile</param>
        /// <returns>Dizionario di coppie di chiavi = nome della variabile, valori = valore della variabile</returns>
        public Dictionary<string, string> ShowVariables(MySqlVariablesType type)
        {
            string t = "";
            if (type == MySqlVariablesType.Global)
                t = "GLOBAL ";
            else if (type == MySqlVariablesType.Session)
                t = "SESSION ";
            DataTable result = readQuery("SHOW " + t + "VARIABLES");
            if (result == null || result.Rows.Count == 0)
                throw new ArgumentException("No " + t + "variables are available");
            Dictionary<string, string> variables = new Dictionary<string, string>();
            foreach (DataRow row in result.Rows)
                variables.Add(row[0].ToString(), row[1].ToString());
            return variables;
        }

        /// <summary>
        /// Imposta un valore alla variabile specificata
        /// </summary>
        /// <param name="variable">Nome della variabile</param>
        /// <param name="type">Tipo di varibile</param>
        /// <param name="val">Nuovo valore della variabile</param>
        public void SetVariable(string variable, MySqlVariablesType type, string val)
        {
            string t = "";
            if (type == MySqlVariablesType.Global)
                t = "GLOBAL ";
            else if (type == MySqlVariablesType.Session)
                t = "SESSION ";
            if (!ExistsVariable(variable, type))
                throw new ArgumentException("The " + t + "variable '" + variable + "' does not exists");
            execQuery("SET " + t + variable + "=" + val);
        }
		
		/// <summary>
		/// Chiude una connessione esistente al database
		/// </summary>
		public void Close()
		{
			if (connection != null)
			{
				connection.Close();
				connection.Dispose();
				setDefaultVariables();
				status = connection_status.invalid;
			}
		}

        /// <summary>
        /// Ottiene un dizionario di coppie di chiavi = numero della riga, valori = lista di valori della riga a partire da una DataTable
        /// </summary>
        /// <param name="data">DataTable da cui estrapolare i valori</param>
        /// <param name="hiddenFieldCount">Indica se nell'estrapolazione dei dati vanno inclusi anche le colonne nascoste</param>
        /// <returns>Dizionario di coppie di chiavi = numero della riga, valori = iista di valori della riga</returns>
        public Dictionary<int, List<string>> GetDictonaryByRow(DataTable data, bool hiddenFieldCount)
        {
            Dictionary<int, List<string>> result = new Dictionary<int, List<string>>();
            if (data.Rows.Count > 0)
            {
                int columncount = data.Columns.Count;
                int rowcount = 0;
                foreach (DataRow row in data.Rows)
                {
                    List<string> values = new List<string>();
                    for (int i = 0; i < columncount; i++)
                    {
                        if (!hiddenFieldCount && data.Columns[i].ColumnMapping == MappingType.Hidden)
                            continue;
                        values.Add((row.IsNull(i)) ? "NULL" : row[i].ToString());
                    }
                    result.Add(rowcount, values);
                    rowcount++;
                }
                return result;
            }
            return result;
        }

        /// <summary>
        /// Ottiene un dizionario di coppie di chiavi = nome delle colonne, valori = lista di valori delle colonne a partire da una DataTable
        /// </summary>
        /// <param name="data">DataTable da cui estrapolare i valori</param>
        /// <param name="hiddenFieldCount">Indica se nell'estrapolazione dei dati vanno inclusi anche le colonne nascoste</param>
        /// <returns>Dizionario di coppie di chiavi = nome delle colonne, valori = lista di valori delle colonne</returns>
        public Dictionary<string, List<string>> GetDictonaryByColumn(DataTable data, bool hiddenFieldCount)
        {
            if (data.Rows.Count > 0)
            {
                Dictionary<string, List<string>> result = new Dictionary<string, List<string>>();
                foreach (DataColumn datacolumn in data.Columns)
                {
                    if (!hiddenFieldCount && datacolumn.ColumnMapping == MappingType.Hidden)
                        continue;
                    List<string> rows = new List<string>();
                    foreach (DataRow row in data.Rows)
                        rows.Add((row.IsNull(datacolumn)) ? "NULL" : row[datacolumn].ToString());
                    result.Add(datacolumn.ColumnName, rows);
                }
                return result;
            }
            return new Dictionary<string, List<string>>();
        }

        /// <summary>
        /// Ottiene una lista dei valori di una colonna a partire da una DataTable
        /// </summary>
        /// <param name="data">DataTable da cui estrapolare i valori</param>
        /// <param name="hiddenFieldCount">Indica se nell'estrapolazione dei dati vanno inclusi anche le colonne nascoste</param>
        /// <returns></returns>
        public List<string> GetList(DataTable data, int column, bool hiddenFieldCount)
        {
            if (data.Rows.Count > 0)
            {
                if (!hiddenFieldCount && data.Columns[0].ColumnMapping == MappingType.Hidden)
                    return new List<string>();
                List<string> result = new List<string>();
                foreach (DataRow row in data.Rows)
                {
                    if (!row.IsNull(column))
                        result.Add(row[column].ToString());
                }
                return result;
            }
            return new List<string>();
        }
		
		#endregion
		
		#region query
		
		/// <summary>
		/// Esegue solo le query che non restituiscono un risultato.
		/// </summary>
		/// <param name="query">Query da eseguire</param>
		public void execQuery(string query)
		{
			if (status == connection_status.invalid)
				throw new ArgumentException("Connection not initialize or credential not correct!");
			try
			{
				connection.Open();
				using (MySqlCommand command = new MySqlCommand())
				{
					command.CommandText = query;
					command.Connection = connection;
					command.ExecuteNonQuery();
				}
				connection.Close();
			} 
			catch (MySqlException err)
			{
				if (connection.State != System.Data.ConnectionState.Closed)
					connection.Close();
				throw new ArgumentException(err.Message);
			}
		}
		
		/// <summary>
		/// Esegue solo le query che restituiscono uno o più risultati
		/// </summary>
		/// <param name="query">Query da eseguire</param>
		/// <returns>Dizionario di coppie di chiavi = nome della colonna, valori = lista dei valori della colonna</returns>
		public DataTable readQuery(string query)
		{
            DataTable schema = new DataTable();
			if (status == connection_status.invalid)
                throw new ArgumentException("Connection not initialize or credential not correct!");
			try
			{	
				connection.Open();
				using (MySqlCommand command = new MySqlCommand())
				{
					command.CommandText = query;
					command.Connection = connection;
                    using (MySqlDataReader reader = command.ExecuteReader()) { schema.Load(reader); }
				}
				connection.Close();
			}
			catch (MySqlException err)
			{
				if (connection.State != System.Data.ConnectionState.Closed)
					connection.Close();
                throw new ArgumentException(err.Message);
			}
            return schema;
		}
		
		#endregion
	}

	#endregion
	
}